package jdklearning.concurrent.thread.yield;


import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * yield：让出时间片，不会释放锁
 *
 * sleep：线程进入睡眠状态，不会释放锁
 *
 * wait：调动方法之前，必须要持有锁。调用了wait()方法以后，锁就会被释放，进入锁的等待队列，方法返回后重新拿到锁
 *
 * notify：调动方法之前，必须要持有锁，调用notify()方法本身不会释放锁的。而是通知等待队列中的某一个线程，同步代码块执行完毕后才会释放锁
 *
 * notifyAll:同notify，有一点不同在于，notifyAll会发出n个信号（n=等待线程数），而notify只会发出一个信号，通常情况下，尽量选择notifyAll
 *
 * 当调用Thread.yield方法时，会给线程调度器一个当前线程愿意让出CPU使用的暗示，但是线程调度器可能会忽略这个暗示。
 *
 * @author shenenlu 2021年03月22日 下午20:30:21
 */
public class YieldLearning {


    /**
     * yield：让出时间片，不会释放锁
     */
    public static void yieldReentrantLockTest(){
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (true){
                        if( "t1".equals(currentThread().getName())){
//                            yield();
                            lock.unlock();
                        }
                        Thread t = currentThread();
                        System.out.println(t.getName()+":"
                                +t.isInterrupted());
                        Thread.sleep(3000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        };
        Thread t2 = new Thread("t2"){
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (true){
                        Thread t = currentThread();
                        System.out.println(t.getName()+":"
                                +t.isInterrupted());
                        Thread.sleep(3000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        };

        t1.start();
        t2.start();
    }


    /**
     * yield：让出时间片，不会释放锁
     */
    public static void yieldSynchronizeTest(){
        Object lock = new Object();
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    synchronized (lock){
                        while (true){
                            if( "t1".equals(currentThread().getName())){
                                yield();
                            }
                            Thread t = currentThread();
                            System.out.println(t.getName()+":"
                                    +t.isInterrupted());
                            Thread.sleep(3000);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t2 = new Thread("t2"){
            @Override
            public void run() {
                try {
                    synchronized (lock){
                        while (true){
                            Thread t = currentThread();
                            System.out.println(t.getName()+":"
                                    +t.isInterrupted());
                            Thread.sleep(3000);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        t1.start();
        t2.start();
    }


    /**
     * 第一种情况：
     *
     * A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,
     * 第二种情况：
     *
     * A0,A1,A2,A3,A4,A5,B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,A6,A7,A8,A9,
     *  
     */
    private static void yieldTest() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                    if (i == 5) {
                        Thread.yield();
                    }
                }
            }
        };

        Thread t1 = new Thread(r,"A");
        Thread t2 = new Thread(r,"B");

        t1.start();
        t2.start();
    }


    /**
     * 下面举个例子，说明一个yield的使用场景（一定是和while()循环搭配使用的）
     *可以看出来，t 线程想要把ai的值从2更新为3，需要等待主线的把ai的值从1更新为2以后
     * 。所以线程 t 在更新ai值失败以后，调用yield方法，让出了cpu的使用权
     * 。如果这时候被调度器忽略的，会继续去更新ai的值。这是ai的值可能是2了，也可能不是
     * 。如果不是，t线程会继续重复上述步骤，直到成功赋值为3。（和我们预期的是一样的）
     * @throws InterruptedException
     */
    public static void yieldCompareAndSet() throws InterruptedException {
        final AtomicInteger ai = new AtomicInteger(1);
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!ai.compareAndSet(2, 3))
                    Thread.yield();//当ai的值还没有被赋值为2的时候，线程让出CPU使用权
            }
        });

        t.start();
        if (ai.compareAndSet(1, 2)) {//将2赋值给ai
            t.join();//等待t线程运行完再往下执行，相当于把线程t的代码加进来了
            if (!t.isAlive()) {
                int a = ai.get();
                System.out.println("a = " + a);//a = 3
            } else {
                System.out.println("t线程还在运行中");//如果t.join()注释掉的话，会打印这句，否则永远不会打印
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        yieldReentrantLockTest();
//        yieldSynchronizeTest();
//        yieldTest();
        yieldCompareAndSet();
    }
}
